#include "stdafx.h"
#include "AdaptationData.h"

using namespace std;

template<class T>
T find(T t1, T t2, string str)
{
	for(; t1!=t2; t1++)
	{
		if(*t1==str) break;
	}
	return t1;
}

AdaptationData::AdaptationData(string fileName)
{
    this->filename = fileName;

    inputBuffer();

    setFieldNames();
}

vector< multimap<string, string, nosort<string> > > AdaptationData::searchAll_()
{
    vector< multimap<string, string, nosort<string> > > res;

    for(list<string>::iterator i = buffer.begin(); i != buffer.end(); i++)
    {
        string row = *i;
        if( row.length() > 4 && row.substr(0, 4) == "$row" )
            res.push_back(createItem_(row));
    }
    return res;
}


vector< multimap <string, string, nosort<string> > > AdaptationData::search(multimap<string, string> criteria)
{
    if( criteria.size() == 0 )
        return searchAll_();

    // buat vektor penampung
    vector< multimap<string, string, nosort<string> > > out;

    // traversal seluruh buffer
    for (list<string>::iterator i = buffer.begin(); i != buffer.end(); i++)
    {
        bool toBeAdded = false;
        multimap<string, string, nosort<string> > m = createItem_(*i);

        if (m.empty())
        {
            toBeAdded = false;
        }
        else
        {
            // traversal kriteria search, pastikan map mengandung semuanya
            for (multimap<string, string>::iterator j = criteria.begin(); j != criteria.end(); j++)
            {
                string key = j->first;
                string value = j->second;
                // Kalau data tidak punya key seperti yg di kriteria, maka lewat..
                if (m.count(key) == 0)
                {
                    toBeAdded = false;
                    break;
                }
                else
                {
                    for( multimap<string, string, nosort<string> >::iterator itr = m.lower_bound(key);
                            itr != m.upper_bound(key); itr++ )
                    {
                        if( itr->second == value )
                            toBeAdded = true;
                    }
                }
            }
        }

        if (toBeAdded)
        {
            out.push_back(m);
        }
    }

    return out;
}

multimap<string, string, nosort<string> >  AdaptationData::createItem_(string row)
{
    multimap<string, string, nosort<string> > item;
    string::size_type idx = row.find('\n');
    if( idx == string::npos )
    {
        return item;
    }

    string::size_type nextIdx = row.find(':', ++idx);
    while( nextIdx != string::npos )
    {
        string key = row.substr(idx, nextIdx-idx);
        idx = nextIdx+2;
        nextIdx = row.find('\n', idx);
        string value = row.substr(idx, nextIdx-idx);
        idx = nextIdx+1;
        item.insert(make_pair(key, value));
        nextIdx = row.find(':', idx);
    }
    return item;
}

string AdaptationData::getValue(string row, string itemName)
{
    return segmentToMap(row)[itemName];
}

bool AdaptationData::update(map<string, string> oldData, map<string, string> newData)
{
    int updatedRows = 0;

    // traversal seluruh buffer
    for (list<string>::iterator i = buffer.begin(); i != buffer.end(); i++)
    {
        bool toBeUpdated = true;
        map<string, string> m = segmentToMap(*i);

        if (m.empty())
        {
            toBeUpdated = false;
        }
        else
        {
            // traversal kriteria search, pastikan map mengandung semuanya
            for (map<string, string>::iterator j = oldData.begin(); (j != oldData.end() && toBeUpdated); j++)
            {
                string key = j->first;
                string value = j->second;

                // Kalau value-nya beda, maka lewat..
                if (m[key] != value)
                {
                    toBeUpdated = false;
                }
            }
        }

        // Kalo lolos kriteria, maka update
        if (toBeUpdated)
        {
            for (map<string, string>::iterator iter = newData.begin(); iter != newData.end(); iter++)
            {
                string key = iter->first;
                string newValue = iter->second;
                m[key] = newValue;
            }
            *i = createRow(m);
            updatedRows++;
        }
    }
    outputBuffer();
    return (updatedRows > 0);
}

void AdaptationData::inputBuffer()
{
    // reset read pointer ke awal file
    fileHandler.open(filename.c_str(), ios_base::in);
    fileHandler.seekg(0, ios_base::beg);

    string segment;
    string baris;
    while (getline(fileHandler, baris))
    {
        if( baris.substr(0, 4) == "$row" )
            segment = baris;
        else if ( baris.substr(0, 4) == "row$" )
        {
            segment += '\n';
            segment += baris;
            buffer.push_back(segment);
        }
        else if( baris[0] == '#' )
        {
            continue;
        }
        else if( baris.size() > 0)
        {
            segment += '\n';
            segment += baris;
        }
    }
    fileHandler.close();
}

void AdaptationData::outputBuffer()
{
    // reset write pointer ke awal file
    fileHandler.open(filename.c_str(), ios_base::out);
    fileHandler.seekp(0, ios_base::beg);

    for (list<string>::iterator iter = buffer.begin(); iter != buffer.end(); iter++)
    {
        fileHandler << *iter << endl;
    }
    fileHandler.close();
}

bool AdaptationData::isValidFieldName(string fieldName)
{
    vector<string>::iterator p=fieldNames.begin();
    string temp;
    while (p!=fieldNames.end())
    {
        temp=*p;
        if (temp==fieldName)
        {
            return true;
        }
        else
        {
            p++;
        }
    }
    return false;
}

bool AdaptationData::isValidFieldName(map<string, string> data)
{
    map<string, string>::iterator p=data.begin();
    string temp;
    while(p!=data.end())
    {
        temp=p->first;
        if (isValidFieldName(temp))
        {
            p++;
        }
        else
        {
            return false;
        }
    }
    return true;
}

bool AdaptationData::isComplete(map<string, string> data)
{
    size_t nbFieldNames=fieldNames.size();
    size_t nbData = data.size();
    if (nbFieldNames!=nbData)
    {
        return false;
    }
    else
    {
        map<string, string>::iterator p = data.begin();
        string tempData;
        while(p!=data.end())
        {
            tempData=p->first;
            if (isValidFieldName(tempData))
            {
                p++;
            }
            else
            {
                return false;
            }
        }
        return true;
    }
}

bool AdaptationData::insert(string row)
{
    list<string>::iterator end;
    end = find(buffer.begin(), buffer.end(), "dataset$");
    list<string>::iterator temp=buffer.insert(end,row);
    outputBuffer();
    if(temp!=buffer.end())
    {
        return true;
    }
    else
    {
        return false;
    }
}

string AdaptationData::createRow(map<string, string> data)
{
    string row="$row\t";
    map<string,string>::iterator p=data.begin();
    string tempItem;
    string tempValue;
    while(p!=data.end())
    {
        tempItem=p->first;
        tempValue=p->second;
        row.append(tempItem);
        row.append(": ");
        row.append(tempValue);
        row.append("\t");
        p++;
    }
    row.append("row$");
    return row;
}

map<string, string> AdaptationData::segmentToMap(string segment)
{
    map<string,string> data;
    if (segment.substr(0,4)=="$row")
    {
        istringstream iss(segment);
        string temp;
        string item;
        string value;
        while(iss>>temp)
        {
            if(temp!="$row"&& temp!="row$")
            {
                if (temp.at(temp.length()-1)==':')
                {
                    item=temp.substr(0,temp.length()-1);
                }
                else
                {
                    value=temp;
                    pair<string,string> pair_(item,value);
                    data.insert(pair_);
                }
            }
        }
    }
    return data;
}

void AdaptationData::setFieldNames()
{
    // cari map yang gak empty
    bool found = false;
    map<string, string> data;

    list<string>::iterator l = buffer.begin();
    while (l != buffer.end() && !found)
    {
        data = segmentToMap(*l);
        if (!data.empty())
        {
            found = true;

            map<string,string>::iterator m=data.begin();
            while(m!=data.end())
            {
                vector<string>::iterator unique=find(fieldNames.begin(),fieldNames.end(),m->first);
                if(unique == fieldNames.end())
                { //tidak ketemu
                    fieldNames.push_back(m->first);
                }
                m++;
            }
        }
        l++;
    }
}
